package com.hht.downloadmanager.down;

import android.app.Activity;
import android.os.Message;
import android.text.TextUtils;
import android.text.format.Formatter;

import com.hht.downloadmanager.bean.FileBean;
import com.hht.downloadmanager.handler.DownLoadHandler;
import com.hht.downloadmanager.handler.DownLoadMessageCall;
import com.hht.downloadmanager.call.UpdateUICall;
import com.hht.downloadmanager.net.OkHttpUtils;
import com.hht.downloadmanager.utils.DownConstants;
import com.hht.downloadmanager.utils.FilesUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.concurrent.atomic.AtomicInteger;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

public class DownLoadManager implements DownLoadMessageCall {

    private String filePath;
    private String fileName;
    private String fileUrl;
    private int threadNumber;
    private FileBean mFileBean;
    private File mTempFile;//临时占位文件，全部下载完成后转为正式文件
    private long mFileCountLength;//下载的文件总大小
    private long[] threadProgress;//每个线程下载对应一个进度，这是所有的进度
    private File[] mCacheFiles;//每个进程都会下载，保存一个缓存文件
    private DownLoadHandler downLoadHandler;
    private long lastTime = 0;
    private AtomicInteger threadFinishInteger = new AtomicInteger(0);//线程安全，用于判断所有线程是否都下载完成
    private AtomicInteger threadPauseInteger = new AtomicInteger(0);//线程安全，用于判断所有线程是否都暂停完成
    private AtomicInteger threadCancelInteger = new AtomicInteger(0);//线程安全，用于判断所有线程是否都取消完成
    private long tempCountProgress = 0;
    private UpdateUICall updateUICall;
    private volatile boolean isDownLoadPause;
    private volatile boolean isOnStart = true;
    private volatile boolean isDownLoadCancel;

    //设置下载暂停
    public void onPause() {
        isDownLoadPause = true;
        isOnStart = true;
    }

    public void onCancel() {
        isDownLoadCancel = true;
        FilesUtil.cleanFile(mTempFile);
    }

    public void setUpdateUICall(UpdateUICall updateUICall) {
        this.updateUICall = updateUICall;
    }


    private DownLoadManager(Activity activity, Builder builder) {
        if (TextUtils.isEmpty(builder.filePath)) {
            this.filePath = FilesUtil.getDefaultFilePath(activity);
        } else {
            this.filePath = builder.filePath;
        }
        if (TextUtils.isEmpty(builder.fileName)) {
            this.fileName = FilesUtil.getDefaultFileName(builder.fileUrl);
        } else {
            this.fileName = builder.fileName;
        }
        if (builder.threadNumber == 0) {
            this.threadNumber = DownConstants.THREAD_NUMBER;
        } else {
            this.threadNumber = builder.threadNumber;
        }
        this.fileUrl = builder.fileUrl;
        if (TextUtils.isEmpty(this.fileUrl)) {
            throw new NullPointerException("fileUrl must not null");
        }

        this.mFileBean = new FileBean(fileName, fileUrl, filePath, threadNumber);
        threadProgress = new long[threadNumber];
        mCacheFiles = new File[threadNumber];
        downLoadHandler = new DownLoadHandler(activity, this);
    }


    @Override
    public void messageCallBack(Message message, Activity activity) {
        switch (message.what) {
            case DownConstants.DOWN_MSG_PROGRESS:
                long mCountProgress = getCurFileLength();
                long curTime = System.currentTimeMillis();
                //每隔一秒，更新一下数据
                if (curTime - lastTime > 1000) {
                    lastTime = curTime;
                    long speed = mCountProgress - tempCountProgress;
                    String curSpeck = Formatter.formatFileSize(activity, speed) + "/s";
                    int curProgress = (int) ((mCountProgress * 100) / mFileCountLength);
                    if (updateUICall != null) {
                        updateUICall.onProgress(mFileCountLength, mCountProgress, curProgress, curSpeck);
                    }
                    tempCountProgress = mCountProgress;
                }
                break;
            case DownConstants.DOWN_MSG_FINISH:
                if (FilesUtil.confirmStatus(threadFinishInteger, mFileBean)) return;
                File file = new File(mFileBean.getFilePath(), mFileBean.getFineName());
                mTempFile.renameTo(file);
                if (updateUICall != null) {
                    updateUICall.onDownLoadFinished(file);
                }
                resetStatue();
                break;
            case DownConstants.DOWN_MSG_PAUSE:
                if (FilesUtil.confirmStatus(threadPauseInteger, mFileBean)) return;
                if (updateUICall != null) {
                    int curProgress = (int) ((getCurFileLength() * 100) / mFileCountLength);
                    updateUICall.onDownLoadPause(mFileCountLength, getCurFileLength(), curProgress);
                }
                resetStatue();
                break;
            case DownConstants.DOWN_MSG_CANCEL:
                if (FilesUtil.confirmStatus(threadCancelInteger, mFileBean)) return;
                threadProgress = new long[mFileBean.getThreadNumber()];
                FilesUtil.cleanFile(mCacheFiles);
                if (updateUICall != null) {
                    updateUICall.onDownLoadCancel();
                }
                resetStatue();
                break;
        }
    }

    private long getCurFileLength() {
        long mCountProgress = 0;//文件下载的总进度
        for (int i = 0; i < threadProgress.length; i++) {
            mCountProgress += threadProgress[i];
        }
        return mCountProgress;
    }

    public synchronized void onStart() {
        if (!isOnStart) {
            return;
        }
        isOnStart = !isOnStart;
        OkHttpUtils.getInstance().getFileContentLength(mFileBean.getFileUrl(), new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
                resetStatue();
            }

            @Override
            public void onResponse(Call call, Response response) {
                try {
                    if (!response.isSuccessful() || response.body() == null) {
                        FilesUtil.closeable(response.body());
                        resetStatue();
                        return;
                    }
                    mFileCountLength = response.body().contentLength();
                    FilesUtil.closeable(response.body());
                    mTempFile = new File(mFileBean.getFilePath(), mFileBean.getFineName() + ".temp");
                    if (!mTempFile.getParentFile().exists()) {
                        mTempFile.getParentFile().mkdirs();
                    }
                    RandomAccessFile tempRandomAccessFile = new RandomAccessFile(mTempFile, "rw");
                    tempRandomAccessFile.setLength(mFileCountLength);
                    //得到每个线程需要下载的数据
                    long blockSize = mFileCountLength / mFileBean.getThreadNumber();
                    //为每个线程分配下载任务
                    for (int i = 0; i < mFileBean.getThreadNumber(); i++) {
                        long startIndex = i * blockSize; // 线程开始下载的位置
                        long endIndex = (i + 1) * blockSize - 1; // 线程结束下载的位置
                        if (i == (mFileBean.getThreadNumber() - 1)) { // 如果是最后一个线程,将剩下的文件全部交给这个线程完成
                            endIndex = mFileCountLength - 1;
                        }
                        downLoadFile(startIndex, endIndex, i);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    resetStatue();
                }
            }
        });
    }

    private void downLoadFile(long startIndex, long endIndex, int threadId) {
        try {
            long newStartIndex = startIndex;
            String cacheFileName = "thread" + threadId + "_" + mFileBean.getFineName();
            final File cacheFile = new File(mFileBean.getFilePath() + "/cache", cacheFileName + ".cache");
            if (!cacheFile.getParentFile().exists()) {
                cacheFile.getParentFile().mkdirs();
            }
            mCacheFiles[threadId] = cacheFile;
            final RandomAccessFile cacheAccessFile = new RandomAccessFile(cacheFile, "rwd");
            if (cacheFile.exists()) {
                //如果文件已下载一部分，读取全部下载的数据，作为开始下载的起点
                try {
                    if (cacheFile.length() > 0) {
                        String readLine = cacheAccessFile.readLine();
                        newStartIndex = Integer.parseInt(readLine);
                    }
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }

            }
            final long finalStartIndex = newStartIndex;
            OkHttpUtils.getInstance().downLoadFileByRange(mFileBean.getFileUrl(), finalStartIndex, endIndex, new Callback() {

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    //206为分段下载返回码
                    if (response.code() != 206) {
                        FilesUtil.closeable(response.body());
                        resetStatue();
                        return;
                    }
                    InputStream inputStream = response.body().byteStream();
                    RandomAccessFile tempRandomAccessFile = new RandomAccessFile(mTempFile, "rw");
                    tempRandomAccessFile.seek(finalStartIndex);//从这个位置开始写入
                    /*  将网络流中的文件写入本地*/
                    byte[] buffer = new byte[1024 << 2];
                    int everyLength = -1;//每次读取的长度
                    int curCount = 0;//这个线程这次一共下载的大小
                    long progress = 0;//本次的进度
                    while ((everyLength = inputStream.read(buffer)) > 0) {
                        if (isDownLoadPause) {
                            FilesUtil.closeable(cacheAccessFile, inputStream, response.body());
                            downLoadHandler.sendEmptyMessage(DownConstants.DOWN_MSG_PAUSE);
                            return;
                        }
                        if (isDownLoadCancel) {
                            FilesUtil.closeable(cacheAccessFile, inputStream, response.body());
                            FilesUtil.cleanFile(cacheFile);
                            downLoadHandler.sendEmptyMessage(DownConstants.DOWN_MSG_CANCEL);
                            return;
                        }

                        tempRandomAccessFile.write(buffer, 0, everyLength);
                        curCount += everyLength;

                        progress = finalStartIndex + curCount;
                        //把当前下载的缓存文件保存到本地上
                        cacheAccessFile.seek(0);
                        cacheAccessFile.write((String.valueOf(progress)).getBytes("UTF-8"));

                        //记录进度消息
                        threadProgress[threadId] = progress - startIndex;
                        downLoadHandler.sendEmptyMessage(DownConstants.DOWN_MSG_PROGRESS);
                    }
                    //当前线程需要下载的文件，已下载完毕，收回资源
                    FilesUtil.closeable(inputStream, tempRandomAccessFile
                            , cacheAccessFile, response.body());
                    FilesUtil.cleanFile(cacheFile);
                    downLoadHandler.sendEmptyMessage(DownConstants.DOWN_MSG_FINISH);
                }

                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                    resetStatue();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
            resetStatue();
        }
    }

    private void resetStatue() {
        isDownLoadCancel = false;
        isDownLoadPause = false;
        isOnStart = true;
    }

    public static class Builder {
        private String filePath;
        private String fileName;
        private String fileUrl;
        private int threadNumber;

        public Builder setThreadNumber(int threadNumber) {
            this.threadNumber = threadNumber;
            return this;
        }

        public Builder setFilePath(String filePath) {
            this.filePath = filePath;
            return this;
        }


        public Builder setFileName(String fileName) {
            this.fileName = fileName;
            return this;
        }


        public Builder setFileUrl(String fileUrl) {
            this.fileUrl = fileUrl;
            return this;
        }

        public DownLoadManager build(Activity activity) {
            return new DownLoadManager(activity, this);
        }
    }
}
